﻿Public Class Form1

    Private lastX As Integer
    Private lastY As Integer
    Private prevClip As Rectangle
    Private CropBox As Rectangle
    Private adjusting As Boolean = False
    Private Cropping As Boolean = False
    Private CurMode As Mode

    Private Enum EdgeType
        Interior
        Exterior
        TopLeft
        Top
        TopRight
        Right
        BottomRight
        Bottom
        BottomLeft
        Left
    End Enum
    Private Class Mode
        Public Edge As EdgeType = EdgeType.Exterior
        Public SizingCursor As Cursor = Cursors.Default
    End Class

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If Not Cropping Then
            Cropping = True
            Button1.Text = "Accept"
            SetCropBox()
        Else
            Cropping = False
            Button1.Text = "Crop"
            PictureBox1.Refresh()
            Dim srcBmp As New Bitmap(PictureBox1.Image)
            Dim bmp As New Bitmap(CropBox.Width, CropBox.Height, srcBmp.PixelFormat)
            Dim g As Graphics = Graphics.FromImage(bmp)
            g.DrawImage(srcBmp, 0, 0, CropBox, GraphicsUnit.Pixel)
            g.Dispose()
            PictureBox2.Image = bmp
        End If
    End Sub

    Private Sub SetCropBox()
        CropBox = New Rectangle(PictureBox1.Width / 8, PictureBox1.Height / 8, PictureBox1.Width * 0.75, PictureBox1.Height * 0.75)
        PictureBox1.Refresh()
    End Sub

    Private Sub PictureBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        If Cropping Then
            If e.Button = MouseButtons.Left Then
                If CurMode.Edge <> EdgeType.Exterior Then
                    Dim p As Point
                    If CurMode.Edge = EdgeType.Interior Then
                        p = PictureBox1.PointToScreen(New Point(e.X, e.Y))
                    Else
                        Select Case CurMode.Edge
                            Case EdgeType.Left
                                p = PictureBox1.PointToScreen(New Point(CropBox.Left, e.Y))
                            Case EdgeType.Top
                                p = PictureBox1.PointToScreen(New Point(e.X, CropBox.Top))
                            Case EdgeType.Right
                                p = PictureBox1.PointToScreen(New Point(CropBox.Right, e.Y))
                            Case EdgeType.Bottom
                                p = PictureBox1.PointToScreen(New Point(e.X, CropBox.Bottom))
                            Case EdgeType.TopLeft
                                p = PictureBox1.PointToScreen(New Point(CropBox.Left, CropBox.Top))
                            Case EdgeType.TopRight
                                p = PictureBox1.PointToScreen(New Point(CropBox.Right, CropBox.Top))
                            Case EdgeType.BottomLeft
                                p = PictureBox1.PointToScreen(New Point(CropBox.Left, CropBox.Bottom))
                            Case EdgeType.BottomRight
                                p = PictureBox1.PointToScreen(New Point(CropBox.Right, CropBox.Bottom))
                        End Select
                    End If

                    Cursor.Position = p
                    lastX = p.X
                    lastY = p.Y
                    adjusting = True
                    PictureBox1.Refresh()
                    Cursor.Hide()
                    prevClip = Cursor.Clip
                    Cursor.Clip = PictureBox1.RectangleToScreen(New Rectangle(0, 0, PictureBox1.Width - 1, PictureBox1.Height - 1))
                    Application.DoEvents()
                    ControlPaint.DrawReversibleFrame(PictureBox1.RectangleToScreen(CropBox), Color.Black, FrameStyle.Dashed)
                End If
            End If
        End If
    End Sub

    Private Sub PictureBox1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        If Cropping Then
            If e.Button = Windows.Forms.MouseButtons.None Then
                CurMode = SetModeAndCursor(e.X, e.Y)
                PictureBox1.Cursor = CurMode.SizingCursor
            ElseIf e.Button = MouseButtons.Left Then
                If CurMode.Edge <> EdgeType.Exterior Then
                    ControlPaint.DrawReversibleFrame(PictureBox1.RectangleToScreen(CropBox), Color.Black, FrameStyle.Dashed)
                    Dim p As Point = PictureBox1.PointToScreen(New Point(e.X, e.Y))
                    Select Case CurMode.Edge
                        Case EdgeType.Interior
                            CropBox.Location = New Point(CropBox.X - (lastX - p.X), CropBox.Y - (lastY - p.Y))
                            If CropBox.X < 0 Then
                                CropBox.X = 0
                            End If
                            If CropBox.Y < 0 Then
                                CropBox.Y = 0
                            End If
                            If CropBox.Right > PictureBox1.ClientRectangle.Right Then
                                CropBox.X = PictureBox1.ClientRectangle.Right - CropBox.Width
                            End If
                            If CropBox.Bottom > PictureBox1.ClientRectangle.Bottom Then
                                CropBox.Y = PictureBox1.ClientRectangle.Bottom - CropBox.Height
                            End If

                        Case EdgeType.Left
                            AdjustLeft(p)

                        Case EdgeType.Right
                            AdjustRight(p)

                        Case EdgeType.Top
                            AdjustTop(p)

                        Case EdgeType.Bottom
                            AdjustBottom(p)

                        Case EdgeType.TopLeft
                            AdjustLeft(p)
                            AdjustTop(p)

                        Case EdgeType.TopRight
                            AdjustRight(p)
                            AdjustTop(p)

                        Case EdgeType.BottomLeft
                            AdjustLeft(p)
                            AdjustBottom(p)

                        Case EdgeType.BottomRight
                            AdjustRight(p)
                            AdjustBottom(p)

                    End Select
                    ControlPaint.DrawReversibleFrame(PictureBox1.RectangleToScreen(CropBox), Color.Black, FrameStyle.Dashed)
                    lastX = p.X
                    lastY = p.Y
                End If
            End If
        End If
    End Sub

    Private Sub AdjustLeft(ByVal p As Point)
        CropBox.X = CropBox.X - (lastX - p.X)
        CropBox.Width = CropBox.Width + (lastX - p.X)
        If CropBox.X < 0 Then
            CropBox.X = 0
        End If
    End Sub

    Private Sub AdjustTop(ByVal p As Point)
        CropBox.Y = CropBox.Y - (lastY - p.Y)
        CropBox.Height = CropBox.Height + (lastY - p.Y)
        If CropBox.Y < 0 Then
            CropBox.Y = 0
        End If
    End Sub

    Private Sub AdjustRight(ByVal p As Point)
        CropBox.Width = CropBox.Width - (lastX - p.X)
        If CropBox.X <= 0 Then
            CropBox.X = 0
        End If
        If CropBox.Right > PictureBox1.ClientRectangle.Right Then
            CropBox.Width = PictureBox1.ClientRectangle.Right - CropBox.Left + 1
        End If
    End Sub

    Private Sub AdjustBottom(ByVal p As Point)
        CropBox.Height = CropBox.Height - (lastY - p.Y)
        If CropBox.Y <= 0 Then
            CropBox.Y = 0
        End If
        If CropBox.Bottom > PictureBox1.ClientRectangle.Bottom Then
            CropBox.Height = PictureBox1.ClientRectangle.Height - CropBox.Top + 1
        End If
    End Sub

    Private Sub PictureBox1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
        If Cropping Then
            If e.Button = MouseButtons.Left Then
                If CurMode.Edge <> EdgeType.Exterior Then
                    Cursor.Show()
                    Cursor.Clip = prevClip
                    ControlPaint.DrawReversibleFrame(PictureBox1.RectangleToScreen(CropBox), Color.Black, FrameStyle.Dashed)
                    CropBox = NormalizeRectangle(CropBox)
                    adjusting = False
                    PictureBox1.Refresh()
                End If
            End If
        End If
    End Sub

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        If Cropping AndAlso Not adjusting Then
            e.Graphics.DrawRectangle(Pens.Red, CropBox)
        End If
    End Sub

    Private Function SetModeAndCursor(ByVal X As Integer, ByVal Y As Integer) As Mode
        Dim M As New Mode ' Exterior / Default

        Static Threshold As Integer = 3
        Dim TopEdge As Boolean = (Math.Abs(Y - CropBox.Top) <= Threshold)
        Dim BottomEdge As Boolean = (Math.Abs(Y - CropBox.Bottom) <= Threshold)
        Dim LeftEdge As Boolean = (Math.Abs(X - CropBox.Left) <= Threshold)
        Dim RightEdge As Boolean = (Math.Abs(X - CropBox.Right) <= Threshold)
        Dim WithinX As Boolean = (X >= CropBox.Left AndAlso X <= CropBox.Right)
        Dim WithinY As Boolean = (Y >= CropBox.Top AndAlso Y <= CropBox.Bottom)

        If CropBox.Contains(X, Y) Then
            M.SizingCursor = Cursors.SizeAll
            M.Edge = EdgeType.Interior
        ElseIf (LeftEdge AndAlso TopEdge) OrElse (RightEdge AndAlso BottomEdge) Then
            M.SizingCursor = Cursors.SizeNWSE
            M.Edge = IIf(TopEdge, EdgeType.TopLeft, EdgeType.BottomRight)
        ElseIf (TopEdge AndAlso RightEdge) OrElse (LeftEdge AndAlso BottomEdge) Then
            M.SizingCursor = Cursors.SizeNESW
            M.Edge = IIf(TopEdge, EdgeType.TopRight, EdgeType.BottomLeft)
        ElseIf WithinX AndAlso (TopEdge OrElse BottomEdge) Then
            M.SizingCursor = Cursors.SizeNS
            M.Edge = IIf(TopEdge, EdgeType.Top, EdgeType.Bottom)
        ElseIf WithinY AndAlso (LeftEdge OrElse RightEdge) Then
            M.SizingCursor = Cursors.SizeWE
            M.Edge = IIf(LeftEdge, EdgeType.Left, EdgeType.Right)
        Else
            M.SizingCursor = Cursors.Default
        End If

        Return M
    End Function

    Private Function NormalizeRectangle(ByVal R As Rectangle) As Rectangle
        Dim normalized As New Rectangle
        normalized.X = Math.Min(R.Left, R.Right)
        normalized.Y = Math.Min(R.Top, R.Bottom)
        normalized.Width = Math.Abs(R.Width)
        normalized.Height = Math.Abs(R.Height)
        Return normalized
    End Function

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        PictureBox1.ImageLocation = "c:\image.jpg"
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        MessageBox.Show("Not implemented yet")
    End Sub
End Class
